<!DOCTYPE html>



  


<html class="theme-next gemini use-motion" lang="zh-CN">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<meta name="theme-color" content="#222">









<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
















  
  
  <link href="/java-notes/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />







<link href="/java-notes/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />

<link href="/java-notes/css/main.css?v=5.1.4" rel="stylesheet" type="text/css" />


  <link rel="apple-touch-icon" sizes="180x180" href="/java-notes/images/apple-touch-icon-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="32x32" href="/java-notes/images/favicon-32x32-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="16x16" href="/java-notes/images/favicon-16x16-next.png?v=5.1.4">


  <link rel="mask-icon" href="/java-notes/images/logo.svg?v=5.1.4" color="#222">





  <meta name="keywords" content="JavaSE,Concurrent," />










<meta name="description" content="并发编程的目的是为了让程序运行得更快，但是，并不是启动更多的线程就能让程序最大限度地并发执行。因为存在上下文切换、死锁等问题">
<meta property="og:type" content="article">
<meta property="og:title" content="Java并发编程(原理)">
<meta property="og:url" content="https://hitomeng.gitee.io/2020/04/19/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/index.html">
<meta property="og:site_name" content="JavaNote">
<meta property="og:description" content="并发编程的目的是为了让程序运行得更快，但是，并不是启动更多的线程就能让程序最大限度地并发执行。因为存在上下文切换、死锁等问题">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://hitomeng.gitee.io/java-notes/2020/04/19/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/JavaRuntimeAarea.png">
<meta property="og:image" content="https://hitomeng.gitee.io/java-notes/2020/04/19/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/.%5CJava%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%5CJavaRuntimeAarea.png">
<meta property="og:image" content="https://hitomeng.gitee.io/java-notes/2020/04/19/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/JMM%E6%8A%BD%E8%B1%A1%E7%A4%BA%E6%84%8F%E5%9B%BE.jpg">
<meta property="og:image" content="https://hitomeng.gitee.io/java-notes/2020/04/19/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/.%5CJava%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%5CJMM%E6%8A%BD%E8%B1%A1%E7%A4%BA%E6%84%8F%E5%9B%BE.jpg">
<meta property="og:image" content="https://hitomeng.gitee.io/java-notes/2020/04/19/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/AQS%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84.png">
<meta property="og:image" content="https://hitomeng.gitee.io/java-notes/2020/04/19/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/.%5CJava%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%5CAQS%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84.png">
<meta property="og:image" content="https://hitomeng.gitee.io/java-notes/2020/04/19/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/acquire%E6%B5%81%E7%A8%8B.jpg">
<meta property="og:image" content="https://hitomeng.gitee.io/java-notes/2020/04/19/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/.%5CJava%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%5Cacquire%E6%B5%81%E7%A8%8B.jpg">
<meta property="article:published_time" content="2020-04-19T15:21:50.000Z">
<meta property="article:modified_time" content="2020-05-25T15:00:33.221Z">
<meta property="article:author" content="HitoMeng">
<meta property="article:tag" content="JavaSE">
<meta property="article:tag" content="Concurrent">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://hitomeng.gitee.io/java-notes/2020/04/19/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/JavaRuntimeAarea.png">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/java-notes/',
    scheme: 'Gemini',
    version: '5.1.4',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    duoshuo: {
      userId: '0',
      author: 'Author'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="https://hitomeng.gitee.io/2020/04/19/Java并发编程/"/>





  <title>Java并发编程(原理) | JavaNote</title>
  








<meta name="generator" content="Hexo 4.2.0"></head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-CN">

  
  
    
  

  <div class="container sidebar-position-left page-post-detail">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/java-notes/"  class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">JavaNote</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle">To be a better developer!</p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/java-notes/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />
            
            Home
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/java-notes/categories/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-th"></i> <br />
            
            Categories
          </a>
        </li>
      
        
        <li class="menu-item menu-item-tags">
          <a href="/java-notes/tags/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br />
            
            Tags
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/java-notes/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
            
            Archives
          </a>
        </li>
      
        
        <li class="menu-item menu-item-about">
          <a href="/java-notes/about/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-user"></i> <br />
            
            About
          </a>
        </li>
      

      
    </ul>
  

  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://hitomeng.gitee.io/java-notes/2020/04/19/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="HitoMeng">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/java-notes/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JavaNote">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">Java并发编程(原理)</h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">Posted on</span>
              
              <time title="Post created" itemprop="dateCreated datePublished" datetime="2020-04-19T23:21:50+08:00">
                2020-04-19
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">In</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/java-notes/categories/High-Concurrent/" itemprop="url" rel="index">
                    <span itemprop="name">High Concurrent</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        <p>并发编程的目的是为了让程序运行得更快，但是，并不是启动更多的线程就能让程序最大限度地并发执行。因为存在上下文切换、死锁等问题</p>
<a id="more"></a>

<h2 id="并发挑战"><a href="#并发挑战" class="headerlink" title="并发挑战"></a>并发挑战</h2><ol>
<li><p><strong>多线程不一定快</strong></p>
<p> 这是因为多线程需要上下文切换的支持，需要一定的资源存储线程切换时地现场信息。 如果数据量较小或计算简单，串行的耗时有可能是小于并发多线程的，那如何解决呢？</p>
<ul>
<li>无锁编程。多线程处理数据时，可以考虑将数据ID按Hash算法分段取模，不同线程处理不同的数据段。<a href="https://mp.weixin.qq.com/s/rjGqxUvrEqJNlo09GrT1Dw" target="_blank" rel="noopener">海量数据处理常见面试题</a>，失效从有道笔记中找</li>
<li>CAS算法。Java的Atomic包使用CAS算法来更新数据，而不需要加锁</li>
<li>协程。在单线程里实现多任务的调度，并在单线程里面维持多个任务的切换。</li>
<li>使用最少线程</li>
</ul>
</li>
<li><p><strong>死锁处理</strong></p>
<p>线程之间因为资源争夺问题，产生的相互等待现象。死锁产生需要四个必要条件</p>
<ol>
<li>互斥条件：同一个资源，同一时间只能被一个进程使用</li>
<li>不可剥夺条件：已经拥有的资源，在使用完毕之前不会被其他线程抢夺</li>
<li>请求和保持条件：进程在拥有部分所需资源时，还可以继续像系统申请剩下所需的资源</li>
<li>循环等待条件：在死锁发生时，一定存在这样一个队列。p1等待p2持有的资源，p2等待p3持有的资源…等待p1持有的资源。</li>
</ol>
<p><strong>破坏掉上面的任意一个或多个条件，就可以预防死锁：</strong></p>
<ul>
<li>资源互斥是无法改变的特质，无法改变互斥条件</li>
<li>破坏不可剥夺条件：进程只有在所有资源都可用的情况下再去运行，否则处于等待状态。哲学家只有左右都有筷子时，才会拿起筷子并就餐。</li>
<li>破坏请求和保持：进程申请不到所需的资源时，释放自己占用的资源</li>
<li>破坏循环等待：为资源编号，按序申请 。结合MySQL中事务之间产生的死锁及解决办法。<a href="https://www.jianshu.com/p/ea718226c56f" target="_blank" rel="noopener">代码参考</a></li>
</ul>
</li>
</ol>
<h2 id="基本概念"><a href="#基本概念" class="headerlink" title="基本概念"></a>基本概念</h2><ol>
<li>进程与线程的区别</li>
<li>线程的状态转换</li>
<li></li>
</ol>
<h2 id="Java并发底层原理"><a href="#Java并发底层原理" class="headerlink" title="Java并发底层原理"></a>Java并发底层原理</h2><h3 id="前置知识点"><a href="#前置知识点" class="headerlink" title="前置知识点"></a>前置知识点</h3><h4 id="Java内存结构"><a href="#Java内存结构" class="headerlink" title="Java内存结构"></a>Java内存结构</h4><img src="/java-notes/2020/04/19/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/JavaRuntimeAarea.png" class title="JavaRuntimeAarea"> 

<p><img src="/java-notes/2020/04/19/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/.%5CJava%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%5CJavaRuntimeAarea.png" alt="Java Runtime Aarea"></p>
<p><strong>PS：黄色区域为所有线程共享，绿色区域为各个线程独有</strong></p>
<ul>
<li>堆： Java堆（Java Heap）是Java虚拟机所管理的内存中<strong><em>最大</em></strong>的一块。Java堆是被所有线程共享的一块内存区域，在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例，<strong><em>几乎所有的对象实例都在这里分配内存</em></strong>。 </li>
<li>方法区：（Method Area）与Java堆一样，是各个线程共享的内存区域，<strong>它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据</strong>。 </li>
<li>运行时常量池： Class文件中除了有类的版本、字段、方法、接口等描述等信息外，还有一项信息是常量池（Constant Pool Table），用于存放编译期生成的各种字面量和符号引用，这部分内容将在类加载后存放到常量池中。  运行时常量是相对于常量来说的，它具备一个重要特征是：动态性。当然，值相同的动态常量与我们通常说的常量只是来源不同，但是都是储存在池内同一块内存区域。Java语言并不要求常量一定只能在编译期产生，运行期间也可能产生新的常量，这些常量被放在运行时常量池中。这里所说的常量包括：基本类型包装类 </li>
<li>程序计数器：（Program Counter Register）是一块较小的内存空间，它的作用可以看做是当前线程所执行的字节码的行号指示器。 <strong>可以从微机系统角度的IP指令指针理解，在JVM层面， 字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令，分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。</strong></li>
<li>JVM栈： 与程序计数器一样，Java虚拟机栈（Java Virtual Machine Stacks）也是线程私有的，<strong>它的生命周期与线程相同</strong>。<strong>虚拟机栈描述的是Java方法执行的内存模型</strong>：每个方法被执行的时候都会同时创建一个栈帧（Stack Frame）用于存储局部变量表、操作栈、动态链接、方法出口等信息。<strong>每一个方法被调用直至执行完成的过程，就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程</strong>。  </li>
<li>本地方法栈： 本地方法栈（Native Method Stacks）与虚拟机栈所发挥的作用是非常相似的，其区别不过是虚拟机栈为虚拟机执行Java方法（也就是字节码）服务，而<strong>本地方法栈则是为虚拟机使用到的Native方法服务</strong>。 </li>
</ul>
<p><strong><em>由上图可知，堆内存是共享的，那么堆中内存会存在不可见问题呢？</em></strong></p>
<h4 id="Java内存模型"><a href="#Java内存模型" class="headerlink" title="Java内存模型"></a>Java内存模型</h4><blockquote>
<p> 堆中内存不可见：这是因为现代计算机为了高效，往往会在高速缓存区中缓存共享变量，因为cpu访问缓存区比访问内存要快得多。</p>
<blockquote>
<p> 线程之间的共享变量存在主内存中，每个线程都有一个私有的本地内存，存储了该线程以读、写共享变量的副本。本地内存是Java内存模型的一个抽象概念，并不真实存在。它涵盖了缓存、写缓冲区、寄存器等。 </p>
</blockquote>
</blockquote>
<p>Java 线程之间的<strong>通信由 Java 内存模型</strong>（简称 JMM ）控制，从抽象的⻆度来说，JMM 定义了线程和主内存之间的抽象关系。 JMM 的抽象示意图如图所示：</p>
<img src="/java-notes/2020/04/19/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/JMM%E6%8A%BD%E8%B1%A1%E7%A4%BA%E6%84%8F%E5%9B%BE.jpg" class title="Java JMM抽象示意图"> 

<p> <img src="/java-notes/2020/04/19/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/.%5CJava%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%5CJMM%E6%8A%BD%E8%B1%A1%E7%A4%BA%E6%84%8F%E5%9B%BE.jpg" alt="img"> </p>
<ul>
<li>主存存放线程需要操作的变量，但线程并不直接操作主存。</li>
<li>每个线程读取主存变量都是先拷贝一份到工作内存中，不同线程工作内存互不干扰。</li>
<li>线程修改了工作内存后，再写回主存中。</li>
</ul>
<p><strong>所以，线程A无法直接访问线程B的工作内存，线程间通信必须经过主内存</strong></p>
<p>根据JMM的规定，<strong>线程对共享变量的所有操作都必须在自己的本地内存中进行，不能直接从主内存中读取</strong>。  所以线程B并不是直接去主内存中读取共享变量的值，而是先在本地内存B中找到这个共享变量，发现这个共享变量已经被更新了，然后本地内存B去主内存中读取这个共享变量的新值，并拷贝到本地内存B中，最后线程B再读取本地内存B中的新值。 </p>
<p>那么怎么知道这个共享变量的被其他线程更新了呢？这就是JMM的功劳了，也是JMM存在的必要性之一。<strong>JMM通过控制主内存与每个线程的本地内存之间的交互，来提供内存可见性保证</strong>。 </p>
<h4 id="重排序"><a href="#重排序" class="headerlink" title="重排序"></a>重排序</h4><blockquote>
<p> 计算机在执行程序时，为了提高性能，编译器和处理器常常会对指令做重排。 </p>
</blockquote>
<p> 指令重排一般分为以下三种： </p>
<ul>
<li><p>编译器优化重排: 编译器在<strong>不改变单线程程序语义</strong>的前提下，可以重新安排语句的执行顺序。  </p>
</li>
<li><p>指令并行重排: 现代处理器采用了指令级并行技术来将多条指令重叠执行。如果<strong>不存在数据依赖性</strong>(即后一个执行的语句无需依赖前面执行的语句的结果)，处理器可以改变语句对应的机器指令的执行顺序。 </p>
</li>
<li><p>内存系统重排: 由于处理器使用缓存和读写缓存冲区，这使得加载(load)和存储(store)操作看上去可能是在乱序执行，因为三级缓存的存在，导致内存与缓存的数据同步存在时间差。 </p>
<p><strong>指令重排可以保证串行语义一致，但是没有义务保证多线程间的语义也一致</strong>。所以在多线程下，指令重排序可能会导致一些问题。 </p>
</li>
</ul>
<h5 id="JMM的保证"><a href="#JMM的保证" class="headerlink" title="JMM的保证"></a>JMM的保证</h5><p> <strong>JMM的具体实现方针是：</strong></p>
<ul>
<li><strong>在不改变（正确同步的）程序执行结果的前提下，尽量为编译期和处理器的优化打开方便之门</strong>。 </li>
<li>对于未同步的多线程程序，JMM只提供<strong>最小安全性</strong>：线程读取到的值，要么是之前某个线程写入的值，要么是默认值，不会无中生有。 </li>
</ul>
<blockquote>
<p> 这里的同步包括了使用<code>volatile</code>、<code>final</code>、<code>synchronized</code>等关键字来实现<strong>多线程下的同步</strong>。 </p>
</blockquote>
<p> JMM没有保证未同步程序的执行结果与该程序在顺序一致性中执行结果一致。因为如果要保证执行结果一致，那么JMM需要禁止大量的优化，对程序的执行性能会产生很大的影响。 </p>
<h4 id="happens-before"><a href="#happens-before" class="headerlink" title="happens-before"></a>happens-before</h4><p> 一方面，程序员需要JMM提供一个强的内存模型来编写代码；另一方面，编译器和处理器希望JMM对它们的束缚越少越好，这样它们就可以最可能多的做优化来提高性能，希望的是一个弱的内存模型。 </p>
<p> JMM考虑了这两种需求，并且找到了平衡点，对编译器和处理器来说，<strong>只要不改变程序的执行结果（单线程程序和正确同步了的多线程程序），编译器和处理器怎么优化都行。</strong> </p>
<p>而对于程序员，JMM提供了<strong>happens-before规则</strong>（JSR-133规范），满足了程序员的需求——<strong>简单易懂，并且提供了足够强的内存可见性保证。</strong>换言之，程序员只要遵循happens-before规则，那他写的程序就能保证在JMM中具有强的内存可见性。 </p>
<p> happens-before关系的定义如下： </p>
<ol>
<li>如果一个操作happens-before另一个操作，那么第一个操作的执行结果将对第二个操作可见，而且第一个操作的执行顺序排在第二个操作之前。 </li>
<li>两个操作之间存在happens-before关系，并不意味着Java平台的具体实现必须要按照happens-before关系指定的顺序来执行。如果重排序之后的执行结果，与按happens-before关系来执行的结果一致，那么JMM也允许这样的重排序。 </li>
</ol>
<p><strong>如果操作A happens-before操作B，那么操作A在内存上所做的操作对操作B都是可见的，不管它们在不在一个线程。</strong> </p>
<p>在Java中，有以下天然的happens-before关系：</p>
<ul>
<li>程序顺序规则：一个线程中的每一个操作，happens-before于该线程中的任意后续操作。</li>
<li>监视器锁规则：对一个锁的解锁，happens-before于随后对这个锁的加锁。</li>
<li>volatile变量规则：对一个volatile域的写，happens-before于任意后续对这个volatile域的读。</li>
<li>传递性：如果A happens-before B，且B happens-before C，那么A happens-before C。</li>
<li>start规则：如果线程A执行操作ThreadB.start()启动线程B，那么A线程的ThreadB.start（）操作happens-before于线程B中的任意操作、</li>
<li>join规则：如果线程A执行操作ThreadB.join（）并成功返回，那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。</li>
</ul>
<h3 id="1-volatile"><a href="#1-volatile" class="headerlink" title="1.volatile"></a>1.volatile</h3><p>volatile是一种轻量且在有限的条件下线程安全技术，它保证修饰的变量的可见性和有序性，<strong>但非原子性</strong>。相对于synchronize高效，而常常跟synchronize配合使用。 先来回忆下基本的概念：</p>
<p>内存可见性: MM有一个主内存，每个线程有自己私有的工作内存，工作内存中保存了一些变量在主内存的拷贝. 内存可见性，指的是线程之间的可见性，当一个线程修改了共享变量时，另一个线程可以读取到这个修改后的值.</p>
<p>重排序： 为优化程序性能，对原有的指令执行顺序进行优化重新排序。重排序可能发生在多个阶段，比如编译重排序、CPU重排序等</p>
<p>happens-before规则： 是一个给程序员使用的规则，只要程序员在写代码的时候遵循happens-before规则，JVM就能保证指令在多线程之间的顺序性符合程序员的预期 </p>
<h4 id="volatile语义"><a href="#volatile语义" class="headerlink" title="volatile语义"></a>volatile语义</h4><ul>
<li><p>保证变量的<strong>内存可见性</strong>： 当一个线程对<code>volatile</code>修饰的变量进行<strong>写操作</strong>时，JMM会立即把该线程对应的本地内存中的共享变量的值刷新到主内存；当一个线程对<code>volatile</code>修饰的变量进行<strong>读操作</strong>时，JMM会把立即该线程对应的本地内存置为无效，从主内存中读取共享变量的值。</p>
</li>
<li><p>禁止volatile变量与普通变量<strong>重排序</strong>：（JSR133提出，Java 5 开始才有这个“增强的volatile内存语义”，提供了一种比锁更轻量级的线程间通信机制。如单写多读模型</p>
<blockquote>
<p> 编译器在<strong>生成字节码时</strong>，会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。编译器选择了一个<strong>比较保守的JMM内存屏障插入策略</strong>，这样可以保证在任何处理器平台，任何程序中都能得到正确的volatile内存语义。这个策略是：</p>
<ul>
<li>在每个volatile写操作前插入一个StoreStore屏障；</li>
<li>在每个volatile写操作后插入一个StoreLoad屏障；</li>
<li>在每个volatile读操作后插入一个LoadLoad屏障；</li>
<li>在每个volatile读操作后再插入一个LoadStore屏障。</li>
</ul>
</blockquote>
</li>
</ul>
<h4 id="volatile与原子性"><a href="#volatile与原子性" class="headerlink" title="volatile与原子性"></a>volatile与原子性</h4><p> volatile只对读或写具有原子性，本身并不对数据运算处理维持原子性，强调的是读写及时影响主存。一般的自增不是单个操作，而是读+写，volatile就无法保证自增操作的原子性。可以使用atomic包下的JDK实现数值运算的原子性。 </p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">TestData</span> </span>&#123;</span><br><span class="line">    <span class="keyword">volatile</span> <span class="keyword">int</span> num = <span class="number">0</span>;</span><br><span class="line">    <span class="comment">//synchronized</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">updateNum</span><span class="params">()</span></span>&#123;</span><br><span class="line">        num++;  <span class="comment">// idea也会提示非原子操作的警告</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">final</span> TestData testData = <span class="keyword">new</span> TestData();</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i &lt;= <span class="number">10</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">                <span class="meta">@Override</span></span><br><span class="line">                <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                    <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">1</span>; j &lt;= <span class="number">1000</span>; j++) &#123;</span><br><span class="line">                        testData.updateNum();</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;).start();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">while</span> (Thread.activeCount() &gt; <span class="number">2</span>) &#123;</span><br><span class="line">            Thread.yield();</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(<span class="string">"最终结果："</span> + testData.num);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p> 你会发现运行结果并不是10000，而是小于期望值。可以多改几次j的最大值查看效果。 因为num++涉及到 read write sync，读到的值一开始时相同的，但写入的时候其他线程可能先写入了。 对于这种只能通过synchronizd关键字处理了。  只要多个线程涉及了读和写，<a href="http://tutorials.jenkov.com/java-concurrency/volatile.html" target="_blank" rel="noopener">volatile is Not Always Enough</a>.一个写，多个读是没有问题的。 </p>
<h4 id="volatile的用途"><a href="#volatile的用途" class="headerlink" title="volatile的用途"></a>volatile的用途</h4><ol>
<li><p>DCL单例操作需要volatile和synchronize保证线程安全 </p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Singleton</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> Singleton instance; <span class="comment">// 不使用volatile关键字</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 双重锁检验</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Singleton <span class="title">getInstance</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (instance == <span class="keyword">null</span>) &#123; <span class="comment">// 第7行</span></span><br><span class="line">            <span class="keyword">synchronized</span> (Singleton<span class="class">.<span class="keyword">class</span>) </span>&#123;</span><br><span class="line">                <span class="keyword">if</span> (instance == <span class="keyword">null</span>) &#123;</span><br><span class="line">                    instance = <span class="keyword">new</span> Singleton(); <span class="comment">// 第10行</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> instance;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>如果这里的变量声明不使用volatile关键字，是可能会发生错误的。它可能会被重排序： </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">instance &#x3D; new Singleton(); &#x2F;&#x2F; 第10行</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F; 可以分解为以下三个步骤</span><br><span class="line">1 memory&#x3D;allocate();&#x2F;&#x2F; 分配内存 相当于c的malloc</span><br><span class="line">2 ctorInstanc(memory) &#x2F;&#x2F;初始化对象</span><br><span class="line">3 s&#x3D;memory &#x2F;&#x2F;设置s指向刚分配的地址</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F; 上述三个步骤可能会被重排序为 1-3-2，也就是：</span><br><span class="line">1 memory&#x3D;allocate();&#x2F;&#x2F; 分配内存 相当于c的malloc</span><br><span class="line">3 s&#x3D;memory &#x2F;&#x2F;设置s指向刚分配的地址</span><br><span class="line">2 ctorInstanc(memory) &#x2F;&#x2F;初始化对象</span><br></pre></td></tr></table></figure>

<p>而一旦假设发生了这样的重排序，比如线程A在第10行执行了步骤1和步骤3，但是步骤2还没有执行完。这个时候线程A执行到了第7行，它会判定instance不为空，然后直接返回了一个未初始化完成的instance </p>
</li>
<li><p>status flag.A线程通知B线程值的改变。</p>
</li>
</ol>
<h3 id="2-synchronized"><a href="#2-synchronized" class="headerlink" title="2.synchronized"></a>2.synchronized</h3><p> Java中提供了两种实现同步的基础语义：<code>synchronized</code>方法和<code>synchronized</code>块 </p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SyncTest</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">syncBlock</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (<span class="keyword">this</span>)&#123;</span><br><span class="line">            System.out.println(<span class="string">"hello block"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">syncMethod</span><span class="params">()</span></span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"hello method"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  public void syncBlock();</span><br><span class="line">    descriptor: ()V</span><br><span class="line">    flags: ACC_PUBLIC</span><br><span class="line">    Code:</span><br><span class="line">      stack=2, locals=3, args_size=1</span><br><span class="line">         0: aload_0</span><br><span class="line">         1: dup</span><br><span class="line">         2: astore_1</span><br><span class="line">         3: monitorenter				 	  // monitorenter指令进入同步块</span><br><span class="line">         4: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;</span><br><span class="line">         7: ldc           #3                  // String hello block</span><br><span class="line">         9: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V</span><br><span class="line">        12: aload_1</span><br><span class="line">        13: monitorexit						  // monitorexit指令退出同步块</span><br><span class="line">        14: goto          22</span><br><span class="line">        17: astore_2</span><br><span class="line">        18: aload_1</span><br><span class="line">        19: monitorexit						  // monitorexit指令退出同步块</span><br><span class="line">        20: aload_2</span><br><span class="line">        21: athrow</span><br><span class="line">        22: return</span><br><span class="line">      Exception table:</span><br><span class="line">         from    to  target type</span><br><span class="line">             4    14    17   any</span><br><span class="line">            17    20    17   any</span><br><span class="line"> </span><br><span class="line"></span><br><span class="line">  public synchronized void syncMethod();</span><br><span class="line">    descriptor: ()V</span><br><span class="line">    flags: ACC_PUBLIC, ACC_SYNCHRONIZED      //添加了ACC_SYNCHRONIZED标记</span><br><span class="line">    Code:</span><br><span class="line">      stack=2, locals=1, args_size=1</span><br><span class="line">         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;</span><br><span class="line">         3: ldc           #5                  // String hello method</span><br><span class="line">         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V</span><br><span class="line">         8: return</span><br><span class="line"> </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>于<code>synchronized</code>代码块而言，<code>javac</code>在编译时，会生成对应的<code>monitorenter</code>和<code>monitorexit</code>指令分别对应<code>synchronized</code>同步块的进入和退出，有两个<code>monitorexit</code>指令的原因是：为了保证抛异常的情况下也能释放锁，所以<code>javac</code>为同步代码块添加了一个隐式的try-finally，在finally中会调用<code>monitorexit</code>命令释放锁。</p>
<p>而对于<code>synchronized</code>方法而言，<code>javac</code>为其生成了一个<code>ACC_SYNCHRONIZED</code>关键字，在JVM进行方法调用时，发现调用的方法被<code>ACC_SYNCHRONIZED</code>修饰，则会先尝试获得锁。 </p>
<p> 在JVM底层，对于这两种<code>synchronized</code>语义的实现大致相同。monitor与一个Java对象绑定，当执行命令时发现monitor时，会尝试获取对象所对应的monitor所有权，<strong>即尝试获得对象的锁</strong>。 </p>
<h4 id="Java对象头"><a href="#Java对象头" class="headerlink" title="Java对象头"></a>Java对象头</h4><p><strong>synchronized用的锁是存在对象头里的。</strong> 每个Java对象都有对象头。如果是非数组类型，则用2个字宽来存储对象头，如果是数组，则会用3个字宽来存储对象头。在32位处理器中，一个字宽是32位；在64位虚拟机中，一个字宽是64位。对象头的内容如下表： </p>
<table>
<thead>
<tr>
<th>长度</th>
<th>内容</th>
<th>说明</th>
</tr>
</thead>
<tbody><tr>
<td>32/64bit</td>
<td>Mark Word</td>
<td>存储对象的hashCode或锁信息等</td>
</tr>
<tr>
<td>32/64bit</td>
<td>Class Metadata Address</td>
<td>存储到对象类型数据的指针</td>
</tr>
<tr>
<td>32/64bit</td>
<td>Array length</td>
<td>数组的长度（如果是数组）</td>
</tr>
</tbody></table>
<h5 id="Mark-Word"><a href="#Mark-Word" class="headerlink" title="Mark Word"></a>Mark Word</h5><p>对象头里面的Mark Word的变化，体现了锁的状态</p>
<p>当对象状态为偏向锁时，<code>Mark Word</code>存储的是偏向的线程ID；</p>
<p>当状态为轻量级锁时，<code>Mark Word</code>存储的是指向线程栈中<code>Lock Record</code>的指针；</p>
<p>当状态为重量级锁时，<code>Mark Word</code>为指向堆中的monitor对象的指针。 </p>
<h4 id="锁的状态"><a href="#锁的状态" class="headerlink" title="锁的状态"></a>锁的状态</h4><ol>
<li><p>偏向锁</p>
<p>Hotspot的作者经过以往的研究发现大多数情况下<strong>锁不仅不存在多线程竞争，而且总是由同一线程多次获得</strong>，于是引入了偏向锁。 偏向锁会偏向于第一个访问锁的线程，如果在接下来的运行过程中，该锁没有被其他的线程访问，则持有偏向锁的线程将永远不需要触发同步。也就是说，<strong>偏向锁在资源无竞争情况下消除了同步语句，连CAS操作都不做了，提高了程序的运行性能。</strong> </p>
</li>
<li><p>轻量级锁： 多个线程在不同时段获取同一把锁，即不存在锁竞争的情况，也就没有线程阻塞。针对这种情况，JVM采用轻量级锁来避免线程的阻塞与唤醒。 </p>
<p>加锁： 如果一个线程获得锁的时候发现是轻量级锁，会把锁的Mark Word复制到自己的Displaced Mark Word里面。  然后线程尝试用CAS将锁的Mark Word替换为指向锁记录的指针。如果成功，当前线程获得锁，如果失败，表示Mark Word已经被替换成了其他线程的锁记录，说明在与其它线程竞争锁，当前线程就尝试使用自旋（循环获取锁）来获取锁。  </p>
<p>自旋是需要消耗CPU的，如果一直获取不到锁的话，那该线程就一直处在自旋状态，白白浪费CPU资源。  DK采用了更聪明的方式——<strong>适应性自旋</strong>，简单来说就是线程如果自旋成功了，则下次自旋的次数会更多，如果自旋失败了，则自旋的次数就会减少。  </p>
<p>自旋也不是一直进行下去的，如果自旋到一定程度（和JVM、操作系统相关），依然没有获取到锁，称为自旋失败，那么这个线程会阻塞。同时这个锁就会<strong>升级成重量级锁</strong>。 </p>
</li>
<li><p>重量级锁</p>
<p>重量级锁依赖于操作系统的互斥量（mutex） 实现的，而操作系统中线程间状态的转换需要相对比较长的时间，所以重量级锁效率很低，但被阻塞的线程不会消耗CPU。 </p>
<p>前面说到，每一个对象都可以当做一个锁，当多个线程同时请求某个对象锁时，对象锁会设置几种状态用来区分请求的线程 ：</p>
<ul>
<li>Contention List：所有请求锁的线程将被首先放置到该竞争队列</li>
<li>Entry List：Contention List中那些有资格成为候选人的线程被移到Entry List</li>
<li>Wait Set：那些调用wait方法被阻塞的线程被放置到Wait Set</li>
<li>OnDeck：任何时刻最多只能有一个线程正在竞争锁，该线程称为OnDeck</li>
<li>Owner：获得锁的线程称为Owner</li>
<li>!Owner：释放锁的线程</li>
</ul>
<p>当一个线程尝试获得锁时，如果该锁已经被占用，则会将该线程封装成一个<code>ObjectWaiter</code>对象插入到<strong>Contention List</strong>的队列的队首，然后调用<code>park</code>函数挂起当前线程。</p>
<p>当线程释放锁时，会从Contention List或EntryList中挑选一个线程唤醒，被选中的线程叫做<code>Heir presumptive</code>即假定继承人，假定继承人被唤醒后会尝试获得锁，但<code>synchronized</code>是非公平的，所以假定继承人不一定能获得锁。这是因为对于重量级锁，线程先自旋尝试获得锁，这样做的目的是为了减少执行操作系统同步操作带来的开销。如果自旋不成功再进入等待队列。这对那些已经在等待队列中的线程来说，稍微显得不公平，还有一个不公平的地方是自旋线程可能会抢占了Ready线程的锁。</p>
<p>如果线程获得锁后调用<code>Object.wait</code>方法，则会将线程加入到WaitSet中，当被<code>Object.notify</code>唤醒后，会将线程从WaitSet移动到Contention List或EntryList中去。需要注意的是，当调用一个锁对象的<code>wait</code>或<code>notify</code>方法时，<strong>如当前锁的状态是偏向锁或轻量级锁则会先膨胀成重量级锁</strong>。</p>
</li>
</ol>
<h4 id="锁的升级过程"><a href="#锁的升级过程" class="headerlink" title="锁的升级过程"></a>锁的升级过程</h4><p>每一个线程在准备获取共享资源时： 第一步，检查MarkWord里面是不是放的自己的ThreadId ,如果是，表示当前线程是处于 “偏向锁” 。</p>
<p>第二步，如果MarkWord不是自己的ThreadId，锁升级，这时候，用CAS来执行切换，新的线程根据MarkWord里面现有的ThreadId，通知之前线程暂停，之前线程将Markword的内容置为空。</p>
<p>第三步，两个线程都把锁对象的HashCode复制到自己新建的用于存储锁的记录空间，接着开始通过CAS操作， 把锁对象的MarKword的内容修改为自己新建的记录空间的地址的方式竞争MarkWord。</p>
<p>第四步，第三步中成功执行CAS的获得资源，失败的则进入自旋 。</p>
<p>第五步，自旋的线程在自旋过程中，成功获得资源(即之前获的资源的线程执行完成并释放了共享资源)，则整个状态依然处于 轻量级锁的状态，如果自旋失败 。</p>
<p>第六步，进入重量级锁的状态，这个时候，自旋的线程进行阻塞，等待之前线程执行完成并唤醒自己。</p>
<h3 id="3-原子操作"><a href="#3-原子操作" class="headerlink" title="3.原子操作"></a>3.原子操作</h3><p>原子本意不可进一步分割的粒子，而原子操作意为不可中断的一个或一系列操作。</p>
<p>在Java中使用锁和循环CAS实现原子操作</p>
<h4 id="CAS"><a href="#CAS" class="headerlink" title="CAS"></a>CAS</h4><p>CAS的全称是：比较并交换（Compare And Swap）。在CAS中，有这样三个值：</p>
<ul>
<li>V：要更新的变量(var)</li>
<li>E：预期值(expected)</li>
<li>N：新值(new)</li>
</ul>
<p>判断V是否等于E，如果等于，将V的值设置为N；如果不等，说明已经有其它线程更新了V，则当前线程放弃更新，什么都不做。 那有没有可能我在判断了V=E之后，正准备更新它的新值的时候，被其它线程更改了V的值呢？不会的。因为CAS是一种原子操作，它是一种系统原语，是一条CPU的原子指令，从CPU层面保证它的原子性</p>
<p><strong>当多个线程同时使用CAS操作一个变量时，只有一个会胜出，并成功更新，其余均会失败，但失败的线程并不会被挂起，仅是被告知失败，并且允许再次尝试，当然也允许失败的线程放弃操作.</strong></p>
<h4 id="Java实现CAS原理-Unsafe类"><a href="#Java实现CAS原理-Unsafe类" class="headerlink" title="Java实现CAS原理-Unsafe类"></a>Java实现CAS原理-Unsafe类</h4><p> 这里我们以<code>AtomicInteger</code>类的<code>getAndAdd(int delta)</code>方法为例，来看看Java是如何实现原子操作的。 </p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 真正依赖的实现</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();</span><br><span class="line"><span class="comment">// 参数中offset，用于获取某个字段相对Java对象的“起始地址”的偏移量</span></span><br><span class="line">private static final long VALUE = U.objectFieldOffset(AtomicInteger.class, "value");</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">int</span> <span class="title">getAndAdd</span><span class="params">(<span class="keyword">int</span> delta)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> U.getAndAddInt(<span class="keyword">this</span>, VALUE, delta);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// Unsafe中被调用的实现</span></span><br><span class="line"><span class="meta">@HotSpotIntrinsicCandidate</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">int</span> <span class="title">getAndAddInt</span><span class="params">(Object o, <span class="keyword">long</span> offset, <span class="keyword">int</span> delta)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> v;</span><br><span class="line">    <span class="keyword">do</span> &#123;</span><br><span class="line">        <span class="comment">// 获取原值，然后在原值基础上更新，更新失败更新原值后再次尝试更新</span></span><br><span class="line">        <span class="comment">// 一直更新失败一直尝试更新</span></span><br><span class="line">        v = getIntVolatile(o, offset);</span><br><span class="line">    &#125; <span class="keyword">while</span> (!weakCompareAndSetInt(o, offset, v, v + delta));</span><br><span class="line">    <span class="keyword">return</span> v;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 循环条件</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">weakCompareAndSetInt</span><span class="params">(Object o, <span class="keyword">long</span> offset,</span></span></span><br><span class="line"><span class="function"><span class="params">                                          <span class="keyword">int</span> expected,</span></span></span><br><span class="line"><span class="function"><span class="params">                                          <span class="keyword">int</span> x)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> compareAndSetInt(o, offset, expected, x);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 最终还是native方法</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">native</span> <span class="keyword">boolean</span> <span class="title">compareAndSetInt</span><span class="params">(Object o, <span class="keyword">long</span> offset,</span></span></span><br><span class="line"><span class="function"><span class="params">                                             <span class="keyword">int</span> expected,</span></span></span><br><span class="line"><span class="function"><span class="params">                                             <span class="keyword">int</span> x)</span></span>;</span><br></pre></td></tr></table></figure>

<p> 看到它是在不断尝试去用CAS更新。如果更新失败，就继续重试。那为什么要把获取“旧值”v的操作放到循环体内呢？其实这也很好理解。前面我们说了，CAS如果旧值V不等于预期值E，它就会更新失败。说明旧的值发生了变化。那我们当然需要返回的是被其他线程改变之后的旧值了，因此放在了do循环体内。 </p>
<h4 id="CAS带来的三大问题"><a href="#CAS带来的三大问题" class="headerlink" title="CAS带来的三大问题"></a>CAS带来的三大问题</h4><ol>
<li><p>ABA问题</p>
<p> 所谓ABA问题，就是一个值原来是A，变成了B，又变回了A。这个时候使用CAS是检查不出变化的，但实际上却被更新了两次。 </p>
<p>ABA问题的解决思路是在变量前面追加上<strong>版本号或者时间戳</strong>。从JDK 1.5开始，JDK的atomic包里提供了一个类<code>AtomicStampedReference</code>类来解决ABA问题。</p>
<p>这个类的<code>compareAndSet</code>方法的作用是首先检查当前引用是否等于预期引用，并且检查当前标志是否等于预期标志，如果二者都相等，才使用CAS设置为新的值和标志。</p>
</li>
<li><p>循环时间长开销大 </p>
<p>CAS多与自旋结合。如果自旋CAS长时间不成功，会占用大量的CPU资源。</p>
<p>解决思路是让JVM支持处理器提供的<strong>pause指令</strong>。</p>
<p>pause指令能让自旋失败时cpu睡眠一小段时间再继续自旋，从而使得读操作的频率低很多,为解决内存顺序冲突而导致的CPU流水线重排的代价也会小很多。</p>
</li>
<li><p>只能保证一个共享变量的原子操作 </p>
<ul>
<li>使用JDK 1.5开始就提供的<code>AtomicReference</code>类保证对象之间的原子性，把多个变量放到一个对象里面进行CAS操作； </li>
</ul>
</li>
</ol>
<ul>
<li>使用锁。锁内的临界区代码可以保证只有当前线程能操作。 </li>
</ul>
<h3 id="4-AQS-AbstractQueuedSynchronizer"><a href="#4-AQS-AbstractQueuedSynchronizer" class="headerlink" title="4. AQS(AbstractQueuedSynchronizer)"></a>4. AQS(AbstractQueuedSynchronizer)</h3><p>AQS是一个用来构建锁和同步器的框架，使用AQS能简单且高效地构造出应用广泛的同步器，比如我们提到的ReentrantLock，Semaphore，ReentrantReadWriteLock，SynchronousQueue，FutureTask等等皆是基于AQS的。  我们自己也能利用AQS非常轻松容易地构造出符合我们自己需求的同步器，只要子类实现它的几个<code>protected</code>方法即可。</p>
<h4 id="数据结构"><a href="#数据结构" class="headerlink" title="数据结构"></a>数据结构</h4><p> AQS内部使用了一个volatile的变量state来作为资源的标识。同时定义了几个获取和改版state的protected方法，子类可以覆盖这些方法来实现自己的逻辑： </p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">getState()</span><br><span class="line">setState()</span><br><span class="line">compareAndSetState()</span><br></pre></td></tr></table></figure>

<p> 这三种均是原子操作，其中compareAndSetState的实现依赖于Unsafe的compareAndSwapInt()方法。 </p>
<p> AQS类本身实现的是一些排队和阻塞的机制，比如具体线程等待队列的维护（如获取资源失败入队/唤醒出队等）。它内部使用了一个先进先出（FIFO）的双端队列，并使用了两个指针head和tail用于标识队列的头部和尾部。其数据结构如图： </p>
<img src="/java-notes/2020/04/19/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/AQS%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84.png" class title="AQS数据结构">

<p> <img src="/java-notes/2020/04/19/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/.%5CJava%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%5CAQS%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84.png" alt="img"> </p>
<p> AQS依赖这个双向队列来完成同步状态的管理。如果当前线程获取同步状态失败，AQS将会将当前线程以及等待状态信息构建成一个节点（Node）并将其加入到同步队列中，同时会阻塞当前线程。当同步状态释放时，会把首节点中的线程唤醒，使其再次获取同步状态。 在CHL中节点（Node）用来保存获取同步状态失败的线程（thread）、等待状态（waitStatus）、前驱节点（prev）和后继节点（next）。 AQS中内部维护的Node节点源码如下： </p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 标记一个结点（对应的线程）在共享模式下等待</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> Node SHARED = <span class="keyword">new</span> Node();</span><br><span class="line">    <span class="comment">// 标记一个结点（对应的线程）在独占模式下等待</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> Node EXCLUSIVE = <span class="keyword">null</span>; </span><br><span class="line"></span><br><span class="line">    <span class="comment">// waitStatus的值，表示该结点（对应的线程）已被取消</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> CANCELLED = <span class="number">1</span>; </span><br><span class="line">    <span class="comment">// waitStatus的值，表示后继结点（对应的线程）需要被唤醒</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> SIGNAL = -<span class="number">1</span>;</span><br><span class="line">    <span class="comment">// waitStatus的值，表示该结点（对应的线程）在等待某一条件</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> CONDITION = -<span class="number">2</span>;</span><br><span class="line">    <span class="comment">/*waitStatus的值，表示有资源可用，新head结点需要继续唤醒后继结点（共享模式下，多线程并发释放资源，而head唤醒其后继结点后，需要把多出来的资源留给后面的结点；设置新的head结点时，会继续唤醒其后继结点）*/</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> PROPAGATE = -<span class="number">3</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待状态，取值范围，-3，-2，-1，0，1</span></span><br><span class="line">    <span class="keyword">volatile</span> <span class="keyword">int</span> waitStatus;</span><br><span class="line">    <span class="keyword">volatile</span> Node prev; <span class="comment">// 前驱结点</span></span><br><span class="line">    <span class="keyword">volatile</span> Node next; <span class="comment">// 后继结点</span></span><br><span class="line">    <span class="keyword">volatile</span> Thread thread; <span class="comment">// 结点对应的线程</span></span><br><span class="line">    Node nextWaiter; <span class="comment">// 等待队列里下一个等待条件的结点</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 判断共享模式的方法</span></span><br><span class="line">    <span class="function"><span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">isShared</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> nextWaiter == SHARED;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    Node(Thread thread, Node mode) &#123;     <span class="comment">// Used by addWaiter</span></span><br><span class="line">        <span class="keyword">this</span>.nextWaiter = mode;</span><br><span class="line">        <span class="keyword">this</span>.thread = thread;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 其它方法忽略，可以参考具体的源码</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// AQS里面的addWaiter私有方法</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> Node <span class="title">addWaiter</span><span class="params">(Node mode)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 快速尝试添加尾节点，如果失败则调用enq(Node node)方法设置尾节点</span></span><br><span class="line">    Node pred = tail;</span><br><span class="line">    <span class="comment">// 判断tail节点是否为空，不为空则添加节点到队列中</span></span><br><span class="line">    <span class="keyword">if</span> (pred != <span class="keyword">null</span>) &#123;</span><br><span class="line">        node.prev = pred;</span><br><span class="line">        <span class="comment">// CAS设置尾节点</span></span><br><span class="line">        <span class="keyword">if</span> (compareAndSetTail(pred, node)) &#123;</span><br><span class="line">            pred.next = node;</span><br><span class="line">            <span class="keyword">return</span> node;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    enq(node);</span><br><span class="line">    <span class="keyword">return</span> node;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 插入节点到队列中.因为需要快速插入到队列尾部，也就解释了为什么是双向队列。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> node the node to insert</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> node's predecessor</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> Node <span class="title">enq</span><span class="params">(<span class="keyword">final</span> Node node)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 死循环 知道将节点插入到队列中为止</span></span><br><span class="line">    <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">        Node t = tail;</span><br><span class="line">        <span class="comment">// 如果队列为空，则首先添加一个空节点到队列中.使用了一个CAS方法（compareAndSetTail(pred, node)）,这里使用CAS的原因是防止在并发添加尾节点的时候出现线程不安全的问题（即有可能出现遗漏节点的情况）。</span></span><br><span class="line">        <span class="keyword">if</span> (t == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (compareAndSetHead(<span class="keyword">new</span> Node()))</span><br><span class="line">                tail = head;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// tail 不为空，则CAS设置尾节点</span></span><br><span class="line">            node.prev = t;</span><br><span class="line">            <span class="keyword">if</span> (compareAndSetTail(t, node)) &#123;</span><br><span class="line">                t.next = node;</span><br><span class="line">                <span class="keyword">return</span> t;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="源码分析"><a href="#源码分析" class="headerlink" title="源码分析"></a>源码分析</h4><p>AQS的设计是基于<strong>模板方法模式</strong>的，它有一些方法必须要子类去实现的，它们主要有：</p>
<ul>
<li>isHeldExclusively()：该线程是否正在独占资源。只有用到condition才需要去实现它。</li>
<li>tryAcquire(int)：独占方式。尝试获取资源，成功则返回true，失败则返回false。</li>
<li>tryRelease(int)：独占方式。尝试释放资源，成功则返回true，失败则返回false。</li>
<li>tryAcquireShared(int)：共享方式。尝试获取资源。负数表示失败；0表示成功，但没有剩余可用资源；正数表示成功，且有剩余资源。</li>
<li>tryReleaseShared(int)：共享方式。尝试释放资源，如果释放后允许唤醒后续等待结点返回true，否则返回false。</li>
</ul>
<h5 id="获取资源"><a href="#获取资源" class="headerlink" title="获取资源"></a>获取资源</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">acquire</span><span class="params">(<span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (!tryAcquire(arg) &amp;&amp;</span><br><span class="line">        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))</span><br><span class="line">        selfInterrupt();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<img src="/java-notes/2020/04/19/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/acquire%E6%B5%81%E7%A8%8B.jpg" class title="acquire流程">

<p> <img src="/java-notes/2020/04/19/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/.%5CJava%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%5Cacquire%E6%B5%81%E7%A8%8B.jpg" alt="img"> </p>
<h5 id="释放资源"><a href="#释放资源" class="headerlink" title="释放资源"></a>释放资源</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">release</span><span class="params">(<span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (tryRelease(arg)) &#123;</span><br><span class="line">        Node h = head;</span><br><span class="line">        <span class="keyword">if</span> (h != <span class="keyword">null</span> &amp;&amp; h.waitStatus != <span class="number">0</span>)</span><br><span class="line">            unparkSuccessor(h);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">unparkSuccessor</span><span class="params">(Node node)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 如果状态是负数，尝试把它设置为0</span></span><br><span class="line">    <span class="keyword">int</span> ws = node.waitStatus;</span><br><span class="line">    <span class="keyword">if</span> (ws &lt; <span class="number">0</span>)</span><br><span class="line">        compareAndSetWaitStatus(node, ws, <span class="number">0</span>);</span><br><span class="line">    <span class="comment">// 得到头结点的后继结点head.next</span></span><br><span class="line">    Node s = node.next;</span><br><span class="line">    <span class="comment">// 如果这个后继结点为空或者状态大于0</span></span><br><span class="line">    <span class="comment">// 通过前面的定义我们知道，大于0只有一种可能，就是这个结点已被取消</span></span><br><span class="line">    <span class="keyword">if</span> (s == <span class="keyword">null</span> || s.waitStatus &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        s = <span class="keyword">null</span>;</span><br><span class="line">        <span class="comment">// 等待队列中所有还有用的结点，都向前移动</span></span><br><span class="line">        <span class="keyword">for</span> (Node t = tail; t != <span class="keyword">null</span> &amp;&amp; t != node; t = t.prev)</span><br><span class="line">            <span class="keyword">if</span> (t.waitStatus &lt;= <span class="number">0</span>)</span><br><span class="line">                s = t;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 如果后继结点不为空，</span></span><br><span class="line">    <span class="keyword">if</span> (s != <span class="keyword">null</span>)</span><br><span class="line">        LockSupport.unpark(s.thread);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/java-notes/tags/JavaSE/" rel="tag"># JavaSE</a>
          
            <a href="/java-notes/tags/Concurrent/" rel="tag"># Concurrent</a>
          
        </div>
      

      
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/java-notes/2020/04/10/sql%E5%BF%85%E7%9F%A5%E5%BF%85%E4%BC%9A/" rel="next" title="sql必知必会">
                <i class="fa fa-chevron-left"></i> sql必知必会
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/java-notes/2020/05/25/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B-%E5%9F%BA%E7%A1%80-%E5%B7%A5%E5%85%B7/" rel="prev" title="Java并发编程-基础&工具">
                Java并发编程-基础&工具 <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          


          

  



        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
            Table of Contents
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
            Overview
          </li>
        </ul>
      

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <p class="site-author-name" itemprop="name">HitoMeng</p>
              <p class="site-description motion-element" itemprop="description">You do have the time!</p>
          </div>

          <nav class="site-state motion-element">

            
              <div class="site-state-item site-state-posts">
              
                <a href="/java-notes/archives/%7C%7Carchive">
              
                  <span class="site-state-item-count">7</span>
                  <span class="site-state-item-name">posts</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-categories">
                <a href="/java-notes/categories/index.html">
                  <span class="site-state-item-count">3</span>
                  <span class="site-state-item-name">categories</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-tags">
                <a href="/java-notes/tags/index.html">
                  <span class="site-state-item-count">8</span>
                  <span class="site-state-item-name">tags</span>
                </a>
              </div>
            

          </nav>

          

          

          
          

          
          

          

        </div>
      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#并发挑战"><span class="nav-number">1.</span> <span class="nav-text">并发挑战</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#基本概念"><span class="nav-number">2.</span> <span class="nav-text">基本概念</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Java并发底层原理"><span class="nav-number">3.</span> <span class="nav-text">Java并发底层原理</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#前置知识点"><span class="nav-number">3.1.</span> <span class="nav-text">前置知识点</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#Java内存结构"><span class="nav-number">3.1.1.</span> <span class="nav-text">Java内存结构</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#Java内存模型"><span class="nav-number">3.1.2.</span> <span class="nav-text">Java内存模型</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#重排序"><span class="nav-number">3.1.3.</span> <span class="nav-text">重排序</span></a><ol class="nav-child"><li class="nav-item nav-level-5"><a class="nav-link" href="#JMM的保证"><span class="nav-number">3.1.3.1.</span> <span class="nav-text">JMM的保证</span></a></li></ol></li><li class="nav-item nav-level-4"><a class="nav-link" href="#happens-before"><span class="nav-number">3.1.4.</span> <span class="nav-text">happens-before</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-volatile"><span class="nav-number">3.2.</span> <span class="nav-text">1.volatile</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#volatile语义"><span class="nav-number">3.2.1.</span> <span class="nav-text">volatile语义</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#volatile与原子性"><span class="nav-number">3.2.2.</span> <span class="nav-text">volatile与原子性</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#volatile的用途"><span class="nav-number">3.2.3.</span> <span class="nav-text">volatile的用途</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-synchronized"><span class="nav-number">3.3.</span> <span class="nav-text">2.synchronized</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#Java对象头"><span class="nav-number">3.3.1.</span> <span class="nav-text">Java对象头</span></a><ol class="nav-child"><li class="nav-item nav-level-5"><a class="nav-link" href="#Mark-Word"><span class="nav-number">3.3.1.1.</span> <span class="nav-text">Mark Word</span></a></li></ol></li><li class="nav-item nav-level-4"><a class="nav-link" href="#锁的状态"><span class="nav-number">3.3.2.</span> <span class="nav-text">锁的状态</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#锁的升级过程"><span class="nav-number">3.3.3.</span> <span class="nav-text">锁的升级过程</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-原子操作"><span class="nav-number">3.4.</span> <span class="nav-text">3.原子操作</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#CAS"><span class="nav-number">3.4.1.</span> <span class="nav-text">CAS</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#Java实现CAS原理-Unsafe类"><span class="nav-number">3.4.2.</span> <span class="nav-text">Java实现CAS原理-Unsafe类</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#CAS带来的三大问题"><span class="nav-number">3.4.3.</span> <span class="nav-text">CAS带来的三大问题</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-AQS-AbstractQueuedSynchronizer"><span class="nav-number">3.5.</span> <span class="nav-text">4. AQS(AbstractQueuedSynchronizer)</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#数据结构"><span class="nav-number">3.5.1.</span> <span class="nav-text">数据结构</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#源码分析"><span class="nav-number">3.5.2.</span> <span class="nav-text">源码分析</span></a><ol class="nav-child"><li class="nav-item nav-level-5"><a class="nav-link" href="#获取资源"><span class="nav-number">3.5.2.1.</span> <span class="nav-text">获取资源</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#释放资源"><span class="nav-number">3.5.2.2.</span> <span class="nav-text">释放资源</span></a></li></ol></li></ol></li></ol></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2020</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">HitoMeng</span>

  
</div>


  <div class="powered-by">Powered by <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a></div>



  <span class="post-meta-divider">|</span>



  <div class="theme-info">Theme &mdash; <a class="theme-link" target="_blank" href="https://github.com/iissnan/hexo-theme-next">NexT.Gemini</a> v5.1.4</div>




        







        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  












  
  
    <script type="text/javascript" src="/java-notes/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/java-notes/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
  

  
  
    <script type="text/javascript" src="/java-notes/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
  

  
  
    <script type="text/javascript" src="/java-notes/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/java-notes/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/java-notes/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
  


  


  <script type="text/javascript" src="/java-notes/js/src/utils.js?v=5.1.4"></script>

  <script type="text/javascript" src="/java-notes/js/src/motion.js?v=5.1.4"></script>



  
  


  <script type="text/javascript" src="/java-notes/js/src/affix.js?v=5.1.4"></script>

  <script type="text/javascript" src="/java-notes/js/src/schemes/pisces.js?v=5.1.4"></script>



  
  <script type="text/javascript" src="/java-notes/js/src/scrollspy.js?v=5.1.4"></script>
<script type="text/javascript" src="/java-notes/js/src/post-details.js?v=5.1.4"></script>



  


  <script type="text/javascript" src="/java-notes/js/src/bootstrap.js?v=5.1.4"></script>



  


  




	





  





  












  





  

  

  

  
  

  

  

  

</body>
</html>
